library ieee;
use ieee.std_logic_1164.all;
use IEEE.NUMERIC_STD.all;
use work.typeDefinitions.all;

entity memStage is
  port (
    clk        : in std_logic;
    nReset     : in std_logic;
    holdStage  : in std_logic;
    invalidate : in std_logic;

    nPC_in         : in std_logic_vector(31 downto 0);
    upper_in       : in std_logic_vector(31 downto 0);
    aluResult_in   : in std_logic_vector(31 downto 0);
    busB_in        : in std_logic_vector(31 downto 0);
    destination_in : in std_logic_vector(4 downto 0);
    rt_in          : in std_logic_vector(4 downto 0);
    instruction_in : in operation;

    memWait   : in  std_logic;
    dMemDataR : in  std_logic_vector(31 downto 0);
    dMemDataW : out std_logic_vector(31 downto 0);
    dMemAddr  : out std_logic_vector(31 downto 0);
    dMemWen   : out std_logic;
    dMemRen   : out std_logic;
    
    -- LINK REGISTER
    linkWen		: out	std_logic; 
    linkAddr	: out std_logic_vector(31 downto 0);
    linkLock	: in std_logic_vector(31 downto 0);

    forwardIn  : in  memForwardIn;
    forwardOut : out memForwardOut;

    upper       : out std_logic_vector(31 downto 0);
    aluResult   : out std_logic_vector(31 downto 0);
    dataMem     : out std_logic_vector(31 downto 0);
    destination : out std_logic_vector(4 downto 0);
    instruction : out operation;
    nPC         : out std_logic_vector(31 downto 0));
end memStage;

architecture memStage_arch of memStage is

  component memInterstageReg
    port (
      clk        : in std_logic;
      nReset     : in std_logic;
      holdStage  : in std_logic;
      invalidate : in std_logic;

      nPC_in         : in std_logic_vector(31 downto 0);
      upper_in       : in std_logic_vector(31 downto 0);
      aluResult_in   : in std_logic_vector(31 downto 0);
      dataMem_in     : in std_logic_vector(31 downto 0);
      destination_in : in std_logic_vector(4 downto 0);

      instruction_in : in operation;

      upper       : out std_logic_vector(31 downto 0);
      aluResult   : out std_logic_vector(31 downto 0);
      dataMem     : out std_logic_vector(31 downto 0);
      destination : out std_logic_vector(4 downto 0);
      instruction : out operation;
      nPC         : out std_logic_vector(31 downto 0));
  end component;

  signal op : operation;
  
  signal aluResult_in_t : std_logic_vector(31 downto 0);
  signal aluResult_toReg : std_logic_vector(31 downto 0);
  signal sc_result : std_logic_vector(31 downto 0);
  
begin  -- fetchInterstageReg  

  op                     <= instruction_in;
  dMemAddr               <= aluResult_in;  --handle "HALTED" business in arbiter
  --forwardOut.aluResult   <= aluResult_in;
  forwardOut.destination <= destination_in;
  forwardOUt.rt          <= rt_in;
  
	-- temp for alu result coming into mem stage
  aluResult_in_t <= aluResult_in;
	-- forwardOut.aluResult chooser
	process( aluResult_in_t, op, sc_result )
	begin
		forwardOut.aluResult <= aluResult_in_t;
		aluResult_toReg <= aluResult_in_t;
		if( op = o_sc ) then
			forwardOut.aluResult <= sc_result;
			aluResult_toReg <= sc_result;
		end if;
	end process;
	
	-- sc_result
	process( aluResult_in_t, linkLock, op )
	begin
		sc_result <= x"00000000"; -- default atomicity fail
		if( op = o_sc ) then
			if( linkLock = aluResult_in_t ) then
				-- atomicity success
				sc_result <= x"0000000" & "0001";
			else
				-- atomicity fail
				sc_result <= x"00000000";
			end if;
		end if;
	end process;
	
  linkAddr <= aluResult_in_t;

  forwB : process (busB_in, forwardIn)
  begin  -- process t_rdat1
    case forwardIn.selB is
      when '1' =>
        dMemDataW <= forwardIn.busB;
      when others =>
        dMemDataW <= busB_in;
    end case;
  end process forwB;

  inter_c : memInterstageReg port map
    (
      clk            => clk,
      nReset         => nReset,
      holdStage      => holdStage,
      invalidate     => invalidate,
      nPC_in         => nPC_in,
      upper_in       => upper_in,
      aluResult_in   => aluResult_toReg,
      dataMem_in     => dMemDataR,
      destination_in => destination_in,
      instruction_in => instruction_in,
      upper          => upper,
      aluresult      => aluResult,
      dataMem        => dataMem,
      destination    => destination,
      instruction    => instruction,
      nPC            => nPC
      );

  memRead : process(op,memWait)
  begin
  	linkWen <= '0';
  	dMemRen <= '0';
    case op is
      when o_lw =>
        dMemRen <= '1';
      when o_ll =>
      	dMemRen <= '1';
      	if( memWait = '0' ) then
      		linkWen <= '1';
      	end if;
      when others =>
        dMemRen <= '0';
    end case;

  end process memRead;

  memWrite : process ( op, sc_result )
  begin  -- process memW
    case op is
      when o_sw =>
        dMemWen <= '1';
      when o_sc =>
      	dmemWen <= '0';
      	if( sc_result = x"0000000"&"0001" ) then
      		dMemWen <= '1';
      	else
      		dmemWen <= '0';
      	end if;
      when others =>
        dMemWen <= '0';
    end case;
  end process memWrite;

end memStage_arch;
